package com.gbiac.modules.monitor.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gbiac.modules.monitor.constant.MonitorConstant;
import com.gbiac.modules.monitor.entity.Rule;
import com.gbiac.modules.monitor.entity.UserCheckIn;
import com.gbiac.modules.monitor.mapper.UserCheckInMapper;
import com.gbiac.modules.monitor.model.dto.AttendanceDTO;
import com.gbiac.modules.monitor.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.SysRoleVo;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: 用户打卡记录
 * @Author: jeecg-boot
 * @Date:   2024-11-30
 * @Version: V1.0
 */
@Service
@Slf4j
public class UserCheckInServiceImpl extends ServiceImpl<UserCheckInMapper, UserCheckIn> implements IUserCheckInService {

    @Resource
    @Lazy
    private ISysBaseAPI sysBaseAPI;

    @Resource
    @Lazy
    private ISeatUserService seatUserService;

    @Resource
    private IRuleService ruleService;

    @Resource
    private ISeatFaceLoginService seatFaceLoginService;

    @Resource
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkIn(String userId, String seatId) {
        UserCheckIn userCheckIn = checkInTemp(userId, seatId);
        // 上岗后，设置当前席位用户
        seatUserService.setSeatUser(seatId, userId, userCheckIn.getCheckInTime());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCheckIn checkInTemp(String userId, String seatId) {
        // 回岗
        LoginUser user = sysBaseAPI.getUserById(userId);

        UserCheckIn userCheckIn = new UserCheckIn();
        userCheckIn.setOrgCode(user.getOrgCode());
        userCheckIn.setUserId(userId);
        userCheckIn.setUsername(user.getUsername());
        userCheckIn.setRealname(user.getRealname());
        userCheckIn.setPhone(user.getPhone());

        List<SysRoleVo> roleList = user.getRoleList();
        if (CollectionUtils.isEmpty(roleList)) {
            throw new JeecgBootException("当前用户没有角色，无法上岗");
        }

        userCheckIn.setRoleCode(roleList.get(0).getRoleCode());
        userCheckIn.setRoleName(roleList.get(0).getRoleName());

        userCheckIn.setSeatId(seatId);
        userCheckIn.setCheckInTime(new Date());

        save(userCheckIn);

        return userCheckIn;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkOut(String userId, String seatId) {
        checkOutTemp(userId, seatId);
        // 离岗后，当前席位清空
        seatUserService.clearSeatUser(seatId);
        // 清空席位人脸登录信息
        seatFaceLoginService.clearLogin(seatId, userId);
        // 下班删除违规记录时间
        redisUtil.del(MonitorConstant.REDIS_AI_PREFIX_RULE_USERS + ":" + userId + ":" + MonitorConstant.REDIS_AI_PREFIX_WARN_TYPE_SLEEP);
        redisUtil.del(MonitorConstant.REDIS_AI_PREFIX_RULE_USERS + ":" + userId + ":" + MonitorConstant.REDIS_AI_PREFIX_WARN_TYPE_LEAVED);
        redisUtil.del(MonitorConstant.REDIS_AI_PREFIX_RULE_USERS + ":" + userId + ":" + MonitorConstant.REDIS_AI_PREFIX_WARN_TYPE_PHONE);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkOutTemp(String userId, String seatId) {
        UserCheckIn userCheckIn = getOne(
                new LambdaQueryWrapper<UserCheckIn>()
                        .eq(UserCheckIn::getUserId, userId)
                        .eq(UserCheckIn::getSeatId, seatId)
                        .isNull(UserCheckIn::getCheckOutTime)
                        .orderByDesc(UserCheckIn::getCheckInTime)
                , false);
        if (userCheckIn == null) {
            return;
        }
        userCheckIn.setCheckOutTime(new Date());
        // 计算总时长
        try {
            calcAttendanceDuration(userCheckIn);
        } catch (Exception e) {
//            e.printStackTrace();
            // 计算总时长失败不要影响下岗
            log.error("{} 计算总时长失败：", JSON.toJSONString(userCheckIn), e);
        }

        updateById(userCheckIn);
    }

    private void calcAttendanceDuration(UserCheckIn userCheckIn) {
        AttendanceDTO attendanceDTO = ruleService.queryAttendanceByOrg(userCheckIn.getOrgCode());

        userCheckIn.setAttendanceDurationDay(calcDuration(userCheckIn.getCheckInTime(), userCheckIn.getCheckOutTime(), attendanceDTO.getDayStartTime().getVal(), attendanceDTO.getDayEndTime().getVal()));
        userCheckIn.setAttendanceDurationNight1(calcDuration(userCheckIn.getCheckInTime(), userCheckIn.getCheckOutTime(), attendanceDTO.getNight1StartTime().getVal(), attendanceDTO.getNight1EndTime().getVal()));
        userCheckIn.setAttendanceDurationNight2(calcDuration(userCheckIn.getCheckInTime(), userCheckIn.getCheckOutTime(), attendanceDTO.getNight2StartTime().getVal(), attendanceDTO.getNight2EndTime().getVal()));

        // 计算总时长
        userCheckIn.setAttendanceDuration(userCheckIn.getAttendanceDurationDay().add(userCheckIn.getAttendanceDurationNight1()).add(userCheckIn.getAttendanceDurationNight2()));
    }

    private BigDecimal calcDuration(Date checkInTime,
                                    Date checkOutTime,
                                    String ruleStartTimeStr,
                                    String ruleEndTimeStr) {
        // 按天切分
        // 上岗时间和离岗时间相差天数
        BigDecimal duration = BigDecimal.ZERO;
        try {
            int dayDiffer = DateUtils.getDayDiffer(checkInTime, checkOutTime);
            Date ruleStartTime = null;
            Date ruleEndTime = null;
            try {
                ruleStartTime = DateUtils.parseDate(DateUtils.formatDate(checkInTime, "yyyy-MM-dd") + " " + ruleStartTimeStr, "yyyy-MM-dd HH:mm");
            } catch (ParseException e) {
                throw new JeecgBootException("考勤规则时间区间格式不对，应为[mm:ss]，实为[" + ruleStartTimeStr + "]");
            }
            try {
                ruleEndTime = DateUtils.parseDate(DateUtils.formatDate(checkInTime, "yyyy-MM-dd") + " " + ruleEndTimeStr, "yyyy-MM-dd HH:mm");
            } catch (ParseException e) {
                throw new JeecgBootException("考勤规则时间区间格式不对，应为[mm:ss]，实为[" + ruleEndTimeStr + "]");
            }
            if (ruleEndTime.before(ruleStartTime)) {
                // 开始时间在结束时间之前，则+1天
                ruleEndTime = DateUtils.addDays(ruleEndTime, 1);
            }
            for (int i = 0; i <= dayDiffer; ) {
                Date newCheckInTime = DateUtils.addDays(DateUtils.parseDate(DateUtils.formatDate(checkInTime, "yyyy-MM-dd") + " 00:00:00", "yyyy-MM-dd HH:mm:ss"), i);
                Date dayOfStart = newCheckInTime;
                Date dayOfEnd = DateUtils.addDays(dayOfStart, 1);

                if (checkInTime.after(dayOfStart) || checkInTime.getTime() == dayOfStart.getTime()) {
                    newCheckInTime = checkInTime;
                }
                Date newCheckOutTime = checkOutTime;
                if (newCheckOutTime.after(dayOfEnd) || newCheckOutTime.getTime() == dayOfEnd.getTime()) {
                    newCheckOutTime = dayOfEnd;
                }

                Date startTime = ruleStartTime.before(newCheckInTime) ? newCheckInTime : ruleStartTime;
                Date endTime = ruleEndTime.before(newCheckOutTime) ? ruleEndTime : newCheckOutTime;
                // 时长（分钟）
                if (startTime.before(endTime)) {
                    duration = duration.add(new BigDecimal((endTime.getTime() - startTime.getTime())).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP));
                }
                if (dayOfEnd.equals(endTime)) {
                    // 结束时间是最后的时间才计算下一天
                    i++;
                }
                if (newCheckOutTime.after(endTime)) {
                    ruleStartTime = DateUtils.addDays(ruleStartTime, 1);
                    ruleEndTime = DateUtils.addDays(ruleEndTime, 1);
                }
                if (checkOutTime.equals(endTime) || checkOutTime.before(ruleStartTime)) {
                    break;
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
            throw new JeecgBootException("时间格式化出错：", e);
        }

        return duration;
    }
}
